#include "StdAfx.h"
#include "NodeMap.h"
#include "MapfileParser.h"
#include "resource.h"

/******************************************************************************
 * CNode Implementation
 *****************************************************************************/
CNode::CNode()
{
    m_pt = CPoint(0, 0);
    m_north = m_east = m_south = m_west = NULL;
    m_type = 0;
    m_id = -1;
    m_numFares = 0;
}

CNode::~CNode()
{
}

CNode& CNode::operator=(const CNode& other)
{
    m_id = other.m_id;
    m_pt = other.m_pt;
    m_north = other.m_north;
    m_south = other.m_south;
    m_east = other.m_east;
    m_west = other.m_west;
    m_type = other.m_type;
    return *this;
}

bool CNode::operator==(const CNode& other)
{
    return (   m_id == other.m_id
            && m_type == other.m_type
            && m_pt == other.m_pt
            && m_north == other.m_north
            && m_south == other.m_south
            && m_east == other.m_east
            && m_west == other.m_west );
}

void CNode::Paint(CPaintDC& dc, bool drawNode, bool drawConnections, bool drawFares)
{
    if (drawNode)
        DrawNode(dc);

    if (drawFares)
        DrawFares(dc, drawNode);

    if (drawConnections)
    {
        DrawLineTo(m_north, dc);
        DrawLineTo(m_south, dc);
        DrawLineTo(m_east, dc);
        DrawLineTo(m_west, dc);
    }
}

void CNode::DrawNode(CPaintDC& dc)
{
    // Create DCs
    CDC* mdc = new CDC;
    mdc->CreateCompatibleDC(&dc);

    CBitmap bmp;
    int id;

    // Select correct bitmap for display
    if (m_type & NF_SELECTED)
    {
        if ((m_type & NF_INOUT) == NF_INOUT)
            id = IDB_INOUT_SEL;
        else if ((m_type & NF_INLET) == NF_INLET)
            id = IDB_INLET_SEL;
        else if ((m_type & NF_OUTLET) == NF_OUTLET)
            id = IDB_OUTLET_SEL;
        else
            id = IDB_NODE_SEL;
    }
    else        
    {
        if ((m_type & NF_INOUT) == NF_INOUT)
            id = IDB_INOUT;
        else if ((m_type & NF_INLET) == NF_INLET)
            id = IDB_INLET;
        else if ((m_type & NF_OUTLET) == NF_OUTLET)
            id = IDB_OUTLET;
        else
            id = IDB_NODE;
    }


    // Load bitmap (only done once, in reality. subsequent loads of the same
    // bitmap will reference the original bitmap)
    bmp.LoadBitmap(id);
    mdc->SelectObject(&bmp);

    // Draw bitmap
    dc.BitBlt(m_pt.x - 4, m_pt.y - 4, 8, 8, mdc, 0, 0, SRCCOPY);
    delete mdc; // Cleanup temporary DC
}

void CNode::DrawLineTo(CNode* n, CPaintDC& dc)
{
    if (n != NULL)
    {
        int offsetX, offsetY;

        // Get x-offset
        if (m_pt.x > n->m_pt.x)
            offsetX = -4;
        else if (m_pt.x < n->m_pt.x)
            offsetX = 4;
        else
            offsetX = 0;

        // Get y-offset
        if (m_pt.y > n->m_pt.y)
            offsetY = -4;
        else if (m_pt.y < n->m_pt.y)
            offsetY = 4;
        else
            offsetY = 0;

        // Check for 2-way street (thicker line)
        int thickness;
        if (n->m_east == this || n->m_north == this || n->m_south == this || n->m_west == this)
            thickness = 2;
        else
            thickness = 1;

        CPen pen(PS_SOLID, thickness, RGB(0, 0, 0));
        CPen* pOldPen = dc.SelectObject(&pen);
        dc.MoveTo(m_pt.x + offsetX, m_pt.y + offsetY);
        dc.LineTo(n->m_pt.x - offsetX, n->m_pt.y - offsetY);
        dc.StrokePath();
        dc.SelectObject(pOldPen);
    }
}

void CNode::DrawFares(CPaintDC& dc, bool nodeDrawn)
{
    if (m_numFares <= 0) return;

    CPen pen1(PS_SOLID, 1, RGB(0, 0, 0));
    CPen pen2(PS_SOLID, 2, RGB(14, 118, 21));
    CBrush brush(RGB(255, 255, 255));

    CPen* oldPen = dc.SelectObject(&pen1);
    CBrush* oldBrush = dc.SelectObject(&brush);

    // Get offsets
    int offx = (m_numFares / 2) * 3 + 4;
    int offy = (nodeDrawn ? 6 : -3);

    // Draw background
    dc.MoveTo(m_pt.x - offx, m_pt.y - offy);
    dc.Rectangle(m_pt.x - offx, m_pt.y - offy,
        m_pt.x - offx + m_numFares * 3 + 3,
        m_pt.y - offy - 7);
    dc.StrokeAndFillPath();

    offx -= 3;
    offy += 3;
    dc.SelectObject(&pen2);

    for (int i = 0; i < m_numFares; i++)
    {
        dc.MoveTo(m_pt.x - offx + (3*i), m_pt.y - offy);
        dc.LineTo(m_pt.x - offx + (3*i), m_pt.y - offy - 2);
        dc.StrokePath();
    }

    dc.SelectObject(oldPen);
}

/******************************************************************************
 * CNodeMap Implementation
 *****************************************************************************/

CNodeMap::CNodeMap()
{
    m_drawNodes = false;
    m_drawSpawns = false;
    m_drawConnections = false;
    m_drawFares = false;
}

CNodeMap::~CNodeMap()
{
    for(CNodePtrVecIter it = m_vecNodes.begin(); it != m_vecNodes.end(); ++it)
    {
        delete *it;
    }

    for (CPathPtrVecIter it = m_vecPaths.begin(); it != m_vecPaths.end(); ++it)
    {
        delete *it;
    }
}

CString CNodeMap::LoadMapFile(const CString& strMapFile)
{
    CString desc = CMapfileParser::Parse(strMapFile, m_vecNodes, m_vecPaths);

    // Save lists of inlets and outlets for later use
    for (CNodePtrVecIter it = m_vecNodes.begin(); it != m_vecNodes.end(); ++it)
    {
        CNode* n = *it;

        if (BITCMP(n->m_type, NF_INLET))
        {
            m_vecInlets.push_back(n);
        }

        if (BITCMP(n->m_type, NF_OUTLET))
        {
            m_vecOutlets.push_back(n);
        }
    }

    return desc;
}

void CNodeMap::Paint(const CRect& rect, CPaintDC& dc)
{
    // Draw nodes
    if (m_drawNodes || m_drawSpawns || m_drawFares)
    {
        for (CNodePtrVecCIter it = m_vecNodes.begin(); it != m_vecNodes.end(); ++it)
        {
            bool isSpawn = BITCMP((*it)->m_type, NF_INLET) 
                || BITCMP((*it)->m_type, NF_OUTLET);
                
            (*it)->Paint(dc, m_drawNodes, m_drawConnections, m_drawFares);
        }
    }
}

/******************************************************************************
 * CPath Implementation
 *****************************************************************************/

CPath::CPath(CNode* src, CNode* dest)
{
    m_src = src;
    m_dest = dest;
}

CPath::~CPath()
{
}

bool CPath::operator ==(const CPath &other)
{
    return (m_dest == other.m_dest && m_src == other.m_src);
}

CPath* CPath::FindPath(int idSrc, int idDest, CPathPtrVec& vecPaths)
{
    for (CPathPtrVecIter it = vecPaths.begin(); it != vecPaths.end(); ++it)
    {
        CPath* path = *it;
        if (path->m_src->m_id == idSrc && path->m_dest->m_id == idDest)
            return path;
    }

    TRACE("Could not find path!");
    ASSERT(FALSE);
    return NULL;
}

void CPath::Add(CNode* node)
{
    m_path.push_back(node);
}

CNode* CPath::GetSource()
{
    return m_src;
}

CNode* CPath::GetDestination()
{
    return m_dest;
}

CNodePtrVecIter CPath::GetIterator()
{
    return m_path.begin() + 1;
}

CNodePtrVec* CPath::GetPath()
{
    return &m_path;
}
